#!/usr/bin/env python
# -*- coding: utf-8 -*-

from typing import Any

import numpy as np

from cta.config.time_sequence_config.rule_config import RuleConfig
from cta.interface.action.close_position.abstract_close_position_action import AbstractClosePositionAction
from web.constants.datetime_format import DatetimeFormat
from web.constants.direction import Direction
from web.constants.k_line_movement_pattern import KLineMovementPattern
from web.domain.responsibility_chain_dto import ResponsibilityChainDto
from web.manager.log_manager import LogManager
from web.models import CommodityFutureInfo
from web.models.commodity_future_date_contract_data import CommodityFutureDateContractData
from web.models.quant2_account import Quant2Account
from web.models.quant2_commodity_future_filter import Quant2CommodityFutureFilter
from web.models.quant2_commodity_future_transaction_record import Quant2CommodityFutureTransactionRecord
from web.util.commodity_future_code_util import CommodityFutureCodeUtil
from web.util.datetime_util import DatetimeUtil
from web.util.list_util import ListUtil

Logger = LogManager.get_logger(__name__)

class RuleClosePositionAction(AbstractClosePositionAction):
    """
    规则策略，平仓期货
    """

    def exec(self, responsibility_chain_dto: ResponsibilityChainDto = None) -> Any:
        Logger.info("规则策略，平仓期货")

        transaction_date: str = DatetimeUtil.datetime_to_str(responsibility_chain_dto, DatetimeFormat.Date_Format)

        quant2_account_list: list[Quant2Account] = self.quant2_account_dao.find_all()
        for quant2_account in quant2_account_list:

            # 查询某个账户的所有持仓记录
            quant2_commodity_future_transaction_record_list: list[Quant2CommodityFutureTransactionRecord] = self.quant2_commodity_future_transaction_record_dao.find_by_account_name_and_not_close_position(quant2_account.account_name)

            # 如果这个账号没有持仓记录，则跳过
            if quant2_commodity_future_transaction_record_list is None or len(quant2_commodity_future_transaction_record_list) == 0:
                Logger.warning("账号[%s]没有持仓记录，不用平仓", quant2_account.account_name)
                continue

            for quant2_commodity_future_transaction_record in quant2_commodity_future_transaction_record_list:
                # 查询期货记录，如果不存在则跳过
                commodity_future_date_contract_data: CommodityFutureDateContractData = self.commodity_future_date_contract_data_dao.find_by_code_and_transaction_date(quant2_commodity_future_transaction_record.code, transaction_date)
                if commodity_future_date_contract_data is None:
                    Logger.warning("期货[%s]在日期[%s]没有交易记录，跳过", commodity_future_date_contract_data.code, transaction_date)
                    continue

                # 趋势形态
                if quant2_commodity_future_transaction_record.k_line_movement_pattern == KLineMovementPattern.Trend:
                    # 牛熊线
                    bull_short_line = None
                    if commodity_future_date_contract_data.ma250 is not None and commodity_future_date_contract_data.bias250 <= RuleConfig.Bias_Threshold_Top \
                            and commodity_future_date_contract_data.bias250 >= RuleConfig.Bias_Threshold_Bottom:
                        bull_short_line = commodity_future_date_contract_data.ma250
                    elif commodity_future_date_contract_data.ma120 is not None and commodity_future_date_contract_data.bias120 <= RuleConfig.Bias_Threshold_Top \
                            and commodity_future_date_contract_data.bias120 >= RuleConfig.Bias_Threshold_Bottom:
                        bull_short_line = commodity_future_date_contract_data.ma120
                    elif commodity_future_date_contract_data.ma60 is not None and commodity_future_date_contract_data.bias60 <= RuleConfig.Bias_Threshold_Top \
                            and commodity_future_date_contract_data.bias60 >= RuleConfig.Bias_Threshold_Bottom:
                        bull_short_line = commodity_future_date_contract_data.ma60
                    elif commodity_future_date_contract_data.ma20 is not None and commodity_future_date_contract_data.bias20 <= RuleConfig.Bias_Threshold_Top \
                            and commodity_future_date_contract_data.bias20 >= RuleConfig.Bias_Threshold_Bottom:
                        bull_short_line = commodity_future_date_contract_data.ma20
                    elif commodity_future_date_contract_data.ma10 is not None and commodity_future_date_contract_data.bias10 <= RuleConfig.Bias_Threshold_Top \
                            and commodity_future_date_contract_data.bias10 >= RuleConfig.Bias_Threshold_Bottom:
                        bull_short_line = commodity_future_date_contract_data.ma10
                    elif commodity_future_date_contract_data.ma5 is not None and commodity_future_date_contract_data.bias5 <= RuleConfig.Bias_Threshold_Top \
                            and commodity_future_date_contract_data.bias5 >= RuleConfig.Bias_Threshold_Bottom:
                        bull_short_line = commodity_future_date_contract_data.ma5
                    elif bull_short_line == None:
                        # 如果最后任何bias指标都无法确定哪一根均线可以作为牛熊线的话，则将前一个交易日的收盘价作为牛熊线。注意：现在数据库中last_close_price字段没有空值
                        bull_short_line = commodity_future_date_contract_data.last_close_price

                    # 趋势向上形态，平多单
                    if commodity_future_date_contract_data.close_price < bull_short_line and quant2_commodity_future_transaction_record.direction == Direction.Up:
                        Logger.info("期货[%s]属于趋势向上形态，现在已经跌破牛熊线，平多单", quant2_commodity_future_transaction_record.code)

                        self.close_long_position_when_trend_pattern(commodity_future_date_contract_data, quant2_commodity_future_transaction_record, transaction_date)

                    # 趋势向下形态，平空单
                    if commodity_future_date_contract_data.close_price > bull_short_line and quant2_commodity_future_transaction_record.direction == Direction.Down:
                        Logger.info("期货[%s]属于趋势向下形态，现在已经突破牛熊线，平空单", quant2_commodity_future_transaction_record.code)

                        self.close_short_position_when_trend_pattern(commodity_future_date_contract_data, quant2_commodity_future_transaction_record, transaction_date)

                # 震荡形态
                if quant2_commodity_future_transaction_record.k_line_movement_pattern == KLineMovementPattern.Fluctuation:

                    # 如果是做多，则出现分型时平多单
                    if quant2_commodity_future_transaction_record.direction == Direction.Up and commodity_future_date_contract_data.close_price > commodity_future_date_contract_data.ma60:
                        self.close_long_position_when_fluctuation_pattern(commodity_future_date_contract_data, quant2_commodity_future_transaction_record, transaction_date)

                    # 如果是做空，则出现分型时平空单
                    if quant2_commodity_future_transaction_record.direction == Direction.Down and commodity_future_date_contract_data.close_price < commodity_future_date_contract_data.ma60:
                        self.close_short_position_when_fluctuation_pattern(commodity_future_date_contract_data, quant2_commodity_future_transaction_record, transaction_date)

                    # # 简单线性回归
                    # commodity_future_date_contact_data_120_list: list[CommodityFutureDateContractData] = self.commodity_future_date_contract_data_dao.find_by_code_and_before_n_transaction_date_order_by_transaction_date_desc(quant2_commodity_future_filter.code, transaction_date, RuleConfig.Linear_Regression_Dimension)
                    # x = np.array(list(map(lambda x: float(x.close_price), commodity_future_date_contact_data_120_list)))
                    # y = np.array(list(range(len(commodity_future_date_contact_data_120_list))))
                    # # 计算斜率和截距
                    # slope, intercept = np.polyfit(x, y, 1)
                    #
                    # if slope >= RuleConfig.Linear_Regression_Slope_Threshold:
                    #     # 震荡向上形态
                    #
                    #     # 如果收盘价在线性回归线的下方，在出现分型形态时，开多单
                    #     if commodity_future_date_contract_data.close_price < slope * float(RuleConfig.Linear_Regression_Dimension - 1) + intercept:
                    #         # 开多单
                    #         self.open_long_position(commodity_future_date_contract_data, quant2_commodity_future_transaction_record,
                    #                                 transaction_date, quant2_account, KLineMovementPattern.Fluctuation)
                    #         quant2_account.hold_commodity_future_number = quant2_account.hold_commodity_future_number - 1
                    #
                    # elif slope <= - RuleConfig.Linear_Regression_Slope_Threshold:
                    #     # 震荡向下形态，应当逢高做空
                    #
                    #     # 如果收盘价在线性回归线的上方，在出现分型形态时，开空单
                    #     if commodity_future_date_contract_data.close_price > slope * float(
                    #             RuleConfig.Linear_Regression_Dimension - 1) + intercept:
                    #         # 开空单
                    #         self.open_short_position(commodity_future_date_contract_data, quant2_commodity_future_filter,
                    #                                  transaction_date, quant2_account, KLineMovementPattern.Fluctuation)
                    #         quant2_account.hold_commodity_future_number = quant2_account.hold_commodity_future_number - 1
                    # else:
                    #     # 水平震荡形态，应当逢低做或逢低做多
                    #
                    #     # 如果收盘价在线性回归线的下方，在出现分型形态时，开多单
                    #     if commodity_future_date_contract_data.close_price < slope * float(
                    #             RuleConfig.Linear_Regression_Dimension - 1) + intercept:
                    #         # 开多单
                    #         self.open_long_position(commodity_future_date_contract_data, quant2_commodity_future_filter,
                    #                                 transaction_date, quant2_account, KLineMovementPattern.Fluctuation)
                    #         quant2_account.hold_commodity_future_number = quant2_account.hold_commodity_future_number - 1
                    #
                    #     # 如果收盘价在线性回归线的上方，在出现分型形态时，开空单
                    #     if commodity_future_date_contract_data.close_price > slope * float(
                    #             RuleConfig.Linear_Regression_Dimension - 1) + intercept:
                    #         # 开空单
                    #         self.open_short_position(commodity_future_date_contract_data, quant2_commodity_future_filter,
                    #                                  transaction_date, quant2_account, KLineMovementPattern.Fluctuation)
                    #         quant2_account.hold_commodity_future_number = quant2_account.hold_commodity_future_number - 1
                    
        super().next(responsibility_chain_dto)
        
    def close_long_position_when_trend_pattern(self, commodity_future_date_contract_data: CommodityFutureDateContractData, quant2_commodity_future_transaction_record: Quant2CommodityFutureTransactionRecord,
                            transaction_date: str):
        """
        趋势向上形态，平多单
        """

        # 查询commodity_future_info记录
        filter_dict = {'code__iexact': CommodityFutureCodeUtil.code_to_contract_code(commodity_future_date_contract_data.code)}
        commodity_future_info: CommodityFutureInfo = self.commodity_future_info_dao.find_one(filter_dict, dict(), list())

        # 更新quant2_c_f_transact_record表的sell_date/buy_date、sell_price/buy_price、ell_lot/buy_lot、profit_and_loss和profit_and_loss_rate字段
        self.quant2_commodity_future_transaction_record_dao.update_when_close_position(
            quant2_commodity_future_transaction_record.direction, transaction_date,
            commodity_future_date_contract_data.close_price, commodity_future_info,
            quant2_commodity_future_transaction_record.buy_lot,
            quant2_commodity_future_transaction_record.id_)
        
        
    def close_short_position_when_trend_pattern(self, commodity_future_date_contract_data: CommodityFutureDateContractData, quant2_commodity_future_transaction_record: Quant2CommodityFutureTransactionRecord,
                            transaction_date: str):
        """
        趋势向下形态，平空单
        """

        # 查询commodity_future_info记录
        filter_dict = {'code__iexact': CommodityFutureCodeUtil.code_to_contract_code(commodity_future_date_contract_data.code)}
        commodity_future_info: CommodityFutureInfo = self.commodity_future_info_dao.find_one(filter_dict, dict(), list())

        # 更新quant2_c_f_transact_record表的sell_date/buy_date、sell_price/buy_price、ell_lot/buy_lot、profit_and_loss和profit_and_loss_rate字段
        self.quant2_commodity_future_transaction_record_dao.update_when_close_position(
            quant2_commodity_future_transaction_record.direction, transaction_date,
            commodity_future_date_contract_data.close_price, commodity_future_info,
            quant2_commodity_future_transaction_record.sell_lot,
            quant2_commodity_future_transaction_record.id_)

    def close_long_position_when_fluctuation_pattern(self, commodity_future_date_contract_data: CommodityFutureDateContractData, quant2_commodity_future_transaction_record: Quant2CommodityFutureTransactionRecord,
                            transaction_date: str):
        """
        震荡形态，平多单
        """

        # 查询上两条交易记录
        last_two_commodity_future_date_contract_data_list: list[CommodityFutureDateContractData] = self.commodity_future_date_contract_data_dao.find_by_code_and_before_n_transaction_date_order_by_transaction_date_desc(commodity_future_date_contract_data.code, transaction_date, 3)
        if last_two_commodity_future_date_contract_data_list is not None and len(last_two_commodity_future_date_contract_data_list) == 3 \
                and last_two_commodity_future_date_contract_data_list[0].close_price < last_two_commodity_future_date_contract_data_list[1].close_price \
                and last_two_commodity_future_date_contract_data_list[1].close_price < last_two_commodity_future_date_contract_data_list[2].close_price:
            Logger.info("期货[%s]震荡形态，平多单", commodity_future_date_contract_data.code)

            # 查询commodity_future_info记录
            filter_dict = {'code__iexact': CommodityFutureCodeUtil.code_to_contract_code(commodity_future_date_contract_data.code)}
            commodity_future_info: CommodityFutureInfo = self.commodity_future_info_dao.find_one(filter_dict, dict(), list())

            # 更新quant2_c_f_transact_record表的sell_date/buy_date、sell_price/buy_price、ell_lot/buy_lot、profit_and_loss和profit_and_loss_rate字段
            self.quant2_commodity_future_transaction_record_dao.update_when_close_position(
                quant2_commodity_future_transaction_record.direction, transaction_date,
                commodity_future_date_contract_data.close_price, commodity_future_info,
                quant2_commodity_future_transaction_record.buy_lot,
                quant2_commodity_future_transaction_record.id_)

    def close_short_position_when_fluctuation_pattern(self, commodity_future_date_contract_data: CommodityFutureDateContractData, quant2_commodity_future_transaction_record: Quant2CommodityFutureTransactionRecord,
                            transaction_date: str):
        """
        震荡形态，平空单
        """

        # 查询上两条交易记录
        last_two_commodity_future_date_contract_data_list: list[CommodityFutureDateContractData] = self.commodity_future_date_contract_data_dao.find_by_code_and_before_n_transaction_date_order_by_transaction_date_desc(commodity_future_date_contract_data.code, transaction_date, 3)
        if last_two_commodity_future_date_contract_data_list is not None and len(last_two_commodity_future_date_contract_data_list) == 3 \
                and last_two_commodity_future_date_contract_data_list[0].close_price > last_two_commodity_future_date_contract_data_list[1].close_price \
                and last_two_commodity_future_date_contract_data_list[1].close_price > last_two_commodity_future_date_contract_data_list[2].close_price:
            Logger.info("期货[%s]震荡形态，平空单", commodity_future_date_contract_data.code)

            # 查询commodity_future_info记录
            filter_dict = {'code__iexact': CommodityFutureCodeUtil.code_to_contract_code(commodity_future_date_contract_data.code)}
            commodity_future_info: CommodityFutureInfo = self.commodity_future_info_dao.find_one(filter_dict, dict(), list())

            # 更新quant2_c_f_transact_record表的sell_date/buy_date、sell_price/buy_price、ell_lot/buy_lot、profit_and_loss和profit_and_loss_rate字段
            self.quant2_commodity_future_transaction_record_dao.update_when_close_position(
                quant2_commodity_future_transaction_record.direction, transaction_date,
                commodity_future_date_contract_data.close_price, commodity_future_info,
                quant2_commodity_future_transaction_record.sell_lot,
                quant2_commodity_future_transaction_record.id_)